<?php

class RSAService
{
    public function sign($privateKey, $data, $signType = "RSA")
    {
        
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($privateKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";

        if (!openssl_pkey_get_private($res)) {
            throw new \Exception('您使用的私钥格式错误，请检查RSA私钥配置');
        }

        if ("RSA2" == $signType) {
            openssl_sign($data, $sign, $res, OPENSSL_ALGO_SHA256);
        } else {
            openssl_sign($data, $sign, $res);
        }

        $sign = base64_encode($sign);
        return $sign;
    }

    public function verify($publicKey, $data, $sign, $signType = 'RSA')
    {
        $res = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($publicKey, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";

        if (!openssl_pkey_get_public($res)) {
            throw new \Exception('RSA公钥错误。请检查公钥格式是否正确');
        }

        //调用openssl内置方法验签，返回bool值
        if ("RSA2" == $signType) {
            $result = (openssl_verify($data, base64_decode($sign), $res, OPENSSL_ALGO_SHA256) === 1);
        } else {
            $result = (openssl_verify($data, base64_decode($sign), $res) === 1);
        }

        return $result;
    }

    public function encrypt($publicKey, $data, $charset = "utf-8")
    {
        $res = "-----BEGIN PUBLIC KEY-----\n" .
            wordwrap($publicKey, 64, "\n", true) .
            "\n-----END PUBLIC KEY-----";

        if (!openssl_pkey_get_public($res)) {
            throw new \Exception('RSA公钥错误。请检查公钥文件格式是否正确');
        }

        $blocks = $this->splitCN($data, 0, 30, $charset);
        $chrtext = null;
        $encodes = array();
        foreach ($blocks as $block) {
            if (!openssl_public_encrypt($block, $chrtext, $res)) {
                throw new \Exception(openssl_error_string());
            }
            $encodes[] = base64_encode($chrtext);
        }

        $chrtext = implode(",", $encodes);
        return base64_encode($chrtext);
    }

    public function decrypt($privateKey, $data)
    {
        $res = "-----BEGIN RSA PRIVATE KEY-----\n" .
            wordwrap($privateKey, 64, "\n", true) .
            "\n-----END RSA PRIVATE KEY-----";

        if (!openssl_pkey_get_private($res)) {
            throw new \Exception('您使用的私钥格式错误，请检查RSA私钥配置');
        }

        $decodes = explode(',', base64_decode($data));
        $strnull = "";
        $dcyCont = "";
        foreach ($decodes as $decode) {
            $decode = base64_decode($decode);
            if (!openssl_private_decrypt($decode, $dcyCont, $res)) {
                throw new \Exception(openssl_error_string());
            }
            $strnull .= $dcyCont;
        }
        return $strnull;
    }

    private function splitCN($cont, $n, $subnum, $charset)
    {
        $arrr = array();
        for ($i = $n; $i < strlen($cont); $i += $subnum) {
            $res = $this->subCNchar($cont, $i, $subnum, $charset);
            if (!empty ($res)) {
                $arrr[] = $res;
            }
        }

        return $arrr;
    }

    private function subCNchar($str, $start, $length, $charset = "gbk")
    {
        if (strlen($str) <= $length) {
            return $str;
        }
        $re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
        $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
        $re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
        $re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
        preg_match_all($re[$charset], $str, $match);
        $slice = join("", array_slice($match[0], $start, $length));
        return $slice;
    }
}